#pragma once
#include "../../Ode.h"
#include "DJointType.h"
#include "DJointParameters.h"

namespace Skill
{
	namespace Ode
	{
		ref class DBody;
		ref class DWorld;
		
		namespace Joints
		{
			ref class DJointFeedback;
			ref class DJointGroup;

			///<remarks>
			/// In real life a joint is something like a hinge, that is used to connect two
			/// objects.
			/// In ODE a joint is very similar: It is a relationship that is enforced between
			/// two bodies so that they can only have certain positions and orientations
			/// relative to each other.
			/// This relationship is called a constraint -- the words joint and
			/// constraint are often used interchangeably.
			///
			/// A joint has a set of parameters that can be set. These include:
			///
			///
			/// \li  dParamLoStop Low stop angle or position. Setting this to
			///	-dInfinity (the default value) turns off the low stop.
			///	For rotational joints, this stop must be greater than -pi to be
			///	effective.
			/// \li  dParamHiStop High stop angle or position. Setting this to
			///	dInfinity (the default value) turns off the high stop.
			///	For rotational joints, this stop must be less than pi to be
			///	effective.
			///	If the high stop is less than the low stop then both stops will
			///	be ineffective.
			/// \li  dParamVel Desired motor velocity (this will be an angular or
			///	linear velocity).
			/// \li  dParamFMax The maximum force or torque that the motor will use to
			///	achieve the desired velocity.
			///	This must always be greater than or equal to zero.
			///	Setting this to zero (the default value) turns off the motor.
			/// \li  dParamFudgeFactor The current joint stop/motor implementation has
			///	a small problem:
			///	when the joint is at one stop and the motor is set to move it away
			///	from the stop, too much force may be applied for one time step,
			///	causing a ``jumping'' motion.
			///	This fudge factor is used to scale this excess force.
			///It should have a value between zero and one (the default value).
			///	If the jumping motion is too visible in a joint, the value can be
			///	reduced.
			///	Making this value too small can prevent the motor from being able to
			///	move the joint away from a stop.
			/// \li  dParamBounce The bouncyness of the stops.
			///	This is a restitution parameter in the range 0..1.
			///	0 means the stops are not bouncy at all, 1 means maximum bouncyness.
			/// \li  dParamCFM The constraint force mixing (CFM) value used when not
			///	at a stop.
			/// \li  dParamStopERP The error reduction parameter (ERP) used by the
			///	stops.
			/// \li  dParamStopCFM The constraint force mixing (CFM) value used by the
			///	stops. Together with the ERP value this can be used to get spongy or
			///	soft stops.
			///	Note that this is intended for unpowered joints, it does not really
			///	work as expected when a powered joint reaches its limit.
			/// \li  dParamSuspensionERP Suspension error reduction parameter (ERP).
			///	Currently this is only implemented on the hinge-2 joint.
			/// \li  dParamSuspensionCFM Suspension constraint force mixing (CFM) value.
			///	Currently this is only implemented on the hinge-2 joint.
			///
			/// If a particular parameter is not implemented by a given joint, setting it
			/// will have no effect.
			/// These parameter names can be optionally followed by a digit (2 or 3)
			/// to indicate the second or third set of parameters, e.g. for the second axis
			/// in a hinge-2 joint, or the third axis in an AMotor joint.
			///</remarks>
			///<summary>
			/// Base class for joints
			///</summary>
			public ref class DJoint abstract: DIUnmanagedPointer
			{
			internal:
				bool _Free;
				bool _Disposed;
				dxJoint* _This;
				void _SetPointer(dxJoint* instance , bool free)
				{
					_This = instance;_Free = free;_Disposed = false;					
				}					
			protected:
				virtual void CollectManagedMemory();
			public:
				   virtual property bool Disposed {bool get(){return _Disposed;}}
				   ~DJoint()
				   {
					   if(!_Disposed)CollectManagedMemory();this->!DJoint();
				   }			

				///<summary>
				/// Destroy a joint.				
				///
				/// disconnects it from its attached bodies and removing it from the world.
				/// However, if the joint is a member of a group then this function has no
				/// effect - to destroy that joint the group must be emptied or destroyed.				
				///</summary>
				!DJoint();
				UNMANAGEDPOINTER_DECLARATION();				

			protected:
				DJoint(DWorld^ world,DJointGroup^ container);
			public:

				///<summary>
				/// Retrieves the world attached to this joint
				///</summary>			
				REF_PROP_DECLARATION_GET(DWorld,World);

				///<summary>
				/// retrieve containing JointGroup
				///</summary>			
				REF_PROP_DECLARATION_GET(DJointGroup,Container);

				///<summary>
				/// Return the number of bodies attached to the joint			
				///</summary>
				VALUE_PROP_DECLARATION_GET(int,NumBodies);

				///<summary>
				/// Attach the joint to some new bodies.			
				///
				/// If the joint is already attached, it will be detached from the old bodies
				/// first.
				/// To attach this joint to only one body, set body1 or body2 to null - a null
				/// body refers to the static environment.
				/// Setting both bodies to zero puts the joint into "limbo", i.e. it will
				/// have no effect on the simulation.
				///</summary>
				///<remarks>			
				/// Some joints, like hinge-2 need to be attached to two bodies to work.
				///</remarks>			
				void Attach(DBody^ body1, DBody^ body2);

				///<summary>
				/// Return the bodies that this joint connects.				
				///</summary>
				///<param name = "index"> return the first (0) or second (1) body. </param>				
				///<remarks>
				/// If one of these returned body IDs is zero, the joint connects the other body
				/// to the static environment.
				/// If both body IDs are zero, the joint is in ``limbo'' and has no effect on
				/// the simulation.
				///</remarks>
				DBody^ GetBody(int index);

				///<summary>
				/// Return the first body
				///</summary>
				REF_PROP_DECLARATION_GET(DBody,Body1);

				///<summary>
				/// Return the second body
				///</summary>
				REF_PROP_DECLARATION_GET(DBody,Body2);

				///<summary>
				/// Gets or set the user-data object
				///</summary>
				REF_PROP_DECLARATION_GETSET(Object,UserData);				

				///<summary>
				/// Get the type of the joint
				///</summary>
				VALUE_PROP_DECLARATION_GET(DJointType,Type);

				

				///<summary>
				///Gets or set the datastructure that is to receive the feedback.				
				/// The feedback can be used by the user, so that it is known how
				/// much force an individual joint exerts.				
				///</summary>
				REF_PROP_DECLARATION_GETSET(DJointFeedback,Feedback);				

				///<summary>
				/// returns connecting joint of given bodies or null if not connecting
				///</summary>
				static DJoint^ GetConnectingJoint(DBody^ body1, DBody^ body2);

				///<summary>
				/// returns number of connecting joint of given bodies
				///</summary>
				static int GetConnectingJointListCount(DBody^ body1, DBody^ body2);

				///<summary>
				/// returns connecting joint of given bodies by index or null if invalid index
				///</summary>
				static DJoint^ GetConnectingJointList(DBody^ body1, DBody^ body2,int index);

				///<summary>				
				/// return true if the two bodies are connected together by
				/// a joint, otherwise return false.
				///</summary>
				static bool AreConnected(DBody^ body1, DBody^ body2);

				///<summary>
				/// return true if the two bodies are connected together by
				/// a joint that does not have type {joint_type}, otherwise return false.
				/// This is useful for deciding whether to add contact joints between two bodies:
				/// if they are already connected by non-contact joints then it may not be
				/// appropriate to add contacts, however it is okay to add more contact between-
				/// bodies that already have contacts.
				///</summary>
				///<param name = "body1"> A body to check. </param>
				///<param name = "body2"> A body to check.</param>
				///<param name = "joint_type"> joint_type is a dJointTypeXXX constant.</param>								
				static bool AreConnectedExcluding(DBody^ body1, DBody^ body2,DJointType joint_type);
			};
		}
	}
}